﻿// QSV2FLV_CPP.cpp : This file contains the 'main' function. Program execution begins and ends there.
//

#include "pch.h"
#include <iostream>
#include "TransCoder.h"
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <windowsx.h>
#include <commctrl.h>
#include <thread>
#include <tchar.h>
#include "Resource.h"
#include "File.h"
#include "Utf8.h"

HINSTANCE gHInstance;
int code = 0;

BOOL    CALLBACK MainDlgProc(HWND, UINT, WPARAM, LPARAM);
int WINAPI _tWinMain(
    _In_     HINSTANCE hInstance,
    _In_opt_ HINSTANCE hPrevInstance,
    _In_     PTSTR     pCmdLine,
    _In_     int       nShowCmd) {

    UNREFERENCED_PARAMETER(hPrevInstance);
    UNREFERENCED_PARAMETER(pCmdLine);
    InitCommonControls();
    gHInstance = hInstance;
    DialogBox(hInstance, L"IDD_HOME", NULL, MainDlgProc);
    return 0;  // WM_QUIT
}

void AddFileToList(HWND hList, std::vector<std::wstring>& files) {
    int itemCount = ListView_GetItemCount(hList);
    LVITEM lvi;

    LVITEM lviRetrive;
    wchar_t path[MAX_PATH] = { 0 };
    lviRetrive.mask = LVIF_TEXT;
    lviRetrive.iSubItem = 1;
    lviRetrive.pszText = path;
    lviRetrive.cchTextMax = MAX_PATH - 1;
    std::vector<std::wstring> itemFiles;
    for (int i = 0; i < itemCount; i++) {
        lviRetrive.iItem = i;
        ListView_GetItem(hList, &lviRetrive);
        itemFiles.push_back(lviRetrive.pszText);
    }

    int skip = 0, origin = itemCount;
    for (int i = 0; i < files.size(); i++) {
        if (std::find(std::begin(itemFiles), std::end(itemFiles), files[i]) != std::end(itemFiles)) {
            skip++;
            continue;
        }

        lvi.mask = LVIF_TEXT | LVIF_PARAM;
        lvi.iItem = origin - skip + i;
        lvi.iSubItem = 0;
        lvi.lParam = (LPARAM)NULL;
        std::wstring index = std::to_wstring(origin - skip + i + 1);
        lvi.pszText = LPWSTR(index.c_str());

        int insertedCount = ListView_InsertItem(hList, &lvi);
        if (insertedCount == -1) {
            MessageBox(hList, L"添加文件出错", L"提示", MB_OK);
            return;
        }
        ListView_SetItemText(hList, origin - skip + i, 1, LPWSTR(files[i].c_str()));
        ListView_SetItemText(hList, origin - skip + i, 2, LPWSTR(L"准备"));
    }
    if (ListView_GetItemCount(hList) > 0) {
        EnableWindow((HWND)GetDlgItem(GetParent(hList), IDB_CLEARLIST), TRUE);
        EnableWindow((HWND)GetDlgItem(GetParent(hList), IDB_STARTCONVERT), TRUE);
    }
}

BOOL CenterWindow(HWND hwndWindow) {
    HWND hwndParent;
    RECT rectWindow, rectParent;

    // make the window relative to its parent
    if ((hwndParent = GetParent(hwndWindow)) != NULL) {
        GetWindowRect(hwndWindow, &rectWindow);
        GetWindowRect(hwndParent, &rectParent);

        int nWidth = rectWindow.right - rectWindow.left;
        int nHeight = rectWindow.bottom - rectWindow.top;

        int nX = ((rectParent.right - rectParent.left) - nWidth) / 2 + rectParent.left;
        int nY = ((rectParent.bottom - rectParent.top) - nHeight) / 2 + rectParent.top;

        int nScreenWidth = GetSystemMetrics(SM_CXSCREEN);
        int nScreenHeight = GetSystemMetrics(SM_CYSCREEN);

        // make sure that the dialog box never moves outside of the screen
        if (nX < 0) nX = 0;
        if (nY < 0) nY = 0;
        if (nX + nWidth > nScreenWidth) nX = nScreenWidth - nWidth;
        if (nY + nHeight > nScreenHeight) nY = nScreenHeight - nHeight;

        MoveWindow(hwndWindow, nX, nY, nWidth, nHeight, FALSE);

        return TRUE;
    }

    return FALSE;
}

BOOL CALLBACK AboutDlgProc(HWND hDlg, UINT message,
                           WPARAM wParam, LPARAM lParam) {
    switch (message) {
    case WM_INITDIALOG: {
        SendMessageW(hDlg, WM_SETICON, ICON_BIG, LPARAM(LoadIcon(gHInstance, L"IDI_ICON1")));
        CenterWindow(hDlg);

        return TRUE;
        break;
    }

    case WM_COMMAND:
        switch (LOWORD(wParam)) {
        case IDOK:
        case IDCANCEL:
            EndDialog(hDlg, 0);
            return TRUE;
        }
        break;
    }
    return FALSE;
}

bool gStopByUser = false;
void Worker(std::vector<std::wstring> itemFiles, HWND hwnd, int msg, int progressInterval, std::wstring outPath) {

    for (int i = 0; i < itemFiles.size(); i++) {
        if (gStopByUser == true) {
            break;
        }
        TransCoder trans(itemFiles[i], outPath);
        trans.TransInfo(hwnd, msg, progressInterval, i);
        ::SendMessage(hwnd, msg, 0, progressInterval + i);
        trans.Transcode();
        auto flvFile = std::wstring(outPath) + L"\\" + File::Basename(itemFiles[i]) + L".flv";
        FlvWriter flvWriter(flvFile, trans.TempFilePath());
        flvWriter.TransInfo(hwnd, msg, progressInterval, i);
        flvWriter.Parse();
        flvWriter.Output();
        File::Delete(trans.TempFilePath());
    }
    gStopByUser = false;
}

BOOL ChangeWndMessageFilterOk(UINT nMessage, BOOL bAllow) {
    typedef BOOL(WINAPI * ChangeWindowMessageFilterOkFn)(UINT, DWORD);

    HMODULE hModUser32 = NULL;
    hModUser32 = LoadLibrary(L"user32.dll");
    if (hModUser32 == NULL) {
        return FALSE;
    }

    ChangeWindowMessageFilterOkFn pfnChangeWindowMessageFilter = (ChangeWindowMessageFilterOkFn)GetProcAddress(hModUser32, "ChangeWindowMessageFilter");
    if (pfnChangeWindowMessageFilter == NULL) {
        FreeLibrary(hModUser32);
        return FALSE;
    }
    FreeLibrary(hModUser32);
    return pfnChangeWindowMessageFilter(nMessage, bAllow ? MSGFLT_ADD : MSGFLT_REMOVE);
}
#define PROGRESS_INTERVAL 10
BOOL CALLBACK MainDlgProc(HWND hDlg, UINT message,
                          WPARAM wParam, LPARAM lParam) {
    switch (message) {
    case WM_INITDIALOG: {
        //使软件可接收文件
        ChangeWndMessageFilterOk(WM_DROPFILES, TRUE);
        ChangeWndMessageFilterOk(0x0049, MSGFLT_ADD);
        DragAcceptFiles(hDlg, TRUE);


        HWND hList = (HWND)GetDlgItem(hDlg, IDBL_ITEMS);

        LV_COLUMN   lvColumn;
        TCHAR       szString[3][20] = { TEXT("序号"), TEXT("文件"), TEXT("状态")};
        ListView_DeleteAllItems(hList);
        //initialize the columns
        lvColumn.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM;
        lvColumn.fmt = LVCFMT_LEFT;

        lvColumn.cx = 55;
        lvColumn.pszText = szString[0];
        ListView_InsertColumn(hList, 0, &lvColumn);

        lvColumn.cx = 404;
        lvColumn.pszText = szString[1];
        ListView_InsertColumn(hList, 1, &lvColumn);

        lvColumn.cx = 63;
        lvColumn.pszText = szString[2];
        ListView_InsertColumn(hList, 2, &lvColumn);

        SendMessageW(hDlg, WM_SETICON, ICON_BIG, LPARAM(LoadIcon(gHInstance, L"IDI_ICON1")));
        HWND hEdit = (HWND)GetDlgItem(hDlg, IDC_DIRECTORY);
        Edit_SetText(hEdit, File::DefaultPathDir().c_str());
        return TRUE;
    }
    break;
    case WM_PROGRESS: {
        int itemIdex = lParam - PROGRESS_INTERVAL, totalInterval = PROGRESS_INTERVAL, currentProgress = wParam;
        HWND hList = (HWND)GetDlgItem(hDlg, IDBL_ITEMS);
        if (totalInterval == currentProgress) {
            ListView_SetItemText(hList, itemIdex, 2, LPWSTR(L"完成"));
            if (ListView_GetItemCount(hList) == (itemIdex + 1)) {
                EnableWindow((HWND)GetDlgItem(hDlg, IDB_STOPCONVERT), FALSE);
                EnableWindow((HWND)GetDlgItem(hDlg, IDB_ADDFILE), TRUE);
                EnableWindow((HWND)GetDlgItem(hDlg, IDB_CLEARLIST), TRUE);
                EnableWindow((HWND)GetDlgItem(hDlg, IDB_STARTCONVERT), TRUE);
            }
        } else {
            auto progressStr = std::to_wstring(currentProgress) + L"/" + std::to_wstring(totalInterval);
            ListView_SetItemText(hList, itemIdex, 2, LPWSTR(progressStr.c_str()));
        }
    }
    break;
    case WM_DROPFILES: {
        HDROP hdrop = (HDROP)wParam;
        UINT  fileCount = ::DragQueryFile(hdrop, (UINT) - 1, NULL, 0);
        TCHAR fileName[MAX_PATH] = { 0 };
        TCHAR longFileName[10 * MAX_PATH];
        DWORD attribute = sizeof(fileName);
        std::vector<std::wstring> filePathVec;
        for (UINT i = 0; i < fileCount; i++) {
            UINT shortLength = ::DragQueryFile((HDROP)hdrop, i, fileName, 10240);
            DWORD longLength = GetLongPathName(fileName, longFileName, 10 * MAX_PATH);
            if (shortLength == longLength) {
                attribute = ::GetFileAttributes(fileName);
                if (!(attribute & FILE_ATTRIBUTE_DIRECTORY)) {
                    if (File::Extension(fileName) == L"qsv") {
                        filePathVec.push_back(fileName);
                    } else {
                        if (i < 3) {
                            std::wstring msg = L"不支持" + File::Name(fileName) + L"的格式";
                            MessageBox(hDlg, msg.c_str(), L"提示", MB_OK);
                        }
                    }
                }
            } else {
                std::wstring msg = File::Name(longFileName) + L"文件路径过长，请修改成较短文件路径";
                MessageBox(hDlg, msg.c_str(), L"提示", MB_OK);
            }
        }
        ::DragFinish(hdrop);

        AddFileToList((HWND)GetDlgItem(hDlg, IDBL_ITEMS), filePathVec);

    }
    break;
    case WM_COMMAND:
        switch (LOWORD(wParam)) {
        case IDOK:
        case IDCANCEL:
            EndDialog(hDlg, 0);
            return TRUE;
        case IDC_OPEN: {
            wchar_t wPath[MAX_PATH] = { 0 };
            Edit_GetText((HWND)GetDlgItem(hDlg, IDC_DIRECTORY), wPath, MAX_PATH);
            File::OpenFileBySystem(wPath);
        }
        break;
        case IDB_ADDFILE: {
            std::wstring filepaths = File::SelectMultiFiles(hDlg, L"爱奇艺视频文件(*.qsv)", L"qsv\0*.qsv");
            if (!filepaths.empty()) {
                std::wstring delimiter = L"\n";
                size_t pos = 0;
                std::wstring filepath;
                TCHAR longFileName[10 * MAX_PATH];
                std::vector<std::wstring> filepathVec;
                while ((pos = filepaths.find(delimiter)) != std::string::npos) {
                    filepath = filepaths.substr(0, pos);
                    DWORD longLength = GetLongPathName(filepath.c_str(), longFileName, 10 * MAX_PATH);
                    if (longLength != filepath.size()) {
                        std::wstring msg = File::Name(longFileName) + L"文件名过长,请更换目录和缩短文件名";
                        MessageBox(hDlg, msg.c_str(), L"提示", MB_OK);
                    } else {
                        filepathVec.push_back(filepath);
                    }
                    filepaths.erase(0, pos + delimiter.length());
                }
                AddFileToList((HWND)GetDlgItem(hDlg, IDBL_ITEMS), filepathVec);
            }
            return TRUE;
        }
        break;
        case IDB_CLEARLIST: {
            ListView_DeleteAllItems((HWND)GetDlgItem(hDlg, IDBL_ITEMS));
            EnableWindow((HWND)GetDlgItem(hDlg, IDB_CLEARLIST), FALSE);
            EnableWindow((HWND)GetDlgItem(hDlg, IDB_STARTCONVERT), FALSE);
        }
        break;
        case IDB_STARTCONVERT: {
            int itemCount = ListView_GetItemCount(GetDlgItem(hDlg, IDBL_ITEMS));
            LVITEM lviRetrive;
            wchar_t path[MAX_PATH] = { 0 };
            lviRetrive.mask = LVIF_TEXT;
            lviRetrive.iSubItem = 1;
            lviRetrive.pszText = path;
            lviRetrive.cchTextMax = MAX_PATH - 1;
            std::vector<std::wstring> itemFiles;

            EnableWindow((HWND)GetDlgItem(hDlg, IDB_STOPCONVERT), TRUE);
            EnableWindow((HWND)GetDlgItem(hDlg, IDB_ADDFILE), FALSE);
            EnableWindow((HWND)GetDlgItem(hDlg, IDB_CLEARLIST), FALSE);
            EnableWindow((HWND)GetDlgItem(hDlg, IDB_STARTCONVERT), FALSE);
            for (int i = 0; i < itemCount; i++) {
                lviRetrive.iItem = i;
                ListView_GetItem(GetDlgItem(hDlg, IDBL_ITEMS), &lviRetrive);
                itemFiles.push_back(lviRetrive.pszText);
            }
            wchar_t wPath[MAX_PATH] = { 0 };
            Edit_GetText((HWND)GetDlgItem(hDlg, IDC_DIRECTORY), wPath, MAX_PATH);
            std::thread worker(Worker, itemFiles, hDlg, WM_PROGRESS, PROGRESS_INTERVAL, wPath);
            worker.detach();
        }
        break;
        case IDB_STOPCONVERT: {
            gStopByUser = true;
            EnableWindow((HWND)GetDlgItem(hDlg, IDB_STOPCONVERT), FALSE);
        }
        break;
        case IDC_BROWSEDIR: {
            HWND hEdit = (HWND)GetDlgItem(hDlg, IDC_DIRECTORY);
            std::wstring selectedDir = File::ChooseDirectory(hDlg);
            if (!selectedDir.empty()) {
                Edit_SetText(hEdit, selectedDir.c_str());
            }
        }
        break;
        case IDB_ABOUT: {
            DialogBox(gHInstance, L"IDD_ABOUT", hDlg, AboutDlgProc);
        }
        break;
        case IDB_DESCRIPTION:
            File::OpenFileBySystem(L"https://gitee.com/napasa/qsv2flv");
            break;
        case IDBL_ITEMS:
            break;
        }
        break;
    }

    return FALSE;
}